package com.wlyy.his.ze.hepler;


import com.alibaba.fastjson.JSON;

import com.wlyy.his.ze.base.ZeApiException;
import com.wlyy.his.ze.config.ZeApiProperties;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.methods.RequestBuilder;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.routing.HttpRoute;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContexts;
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.springframework.http.HttpMethod;

import javax.net.ssl.SSLContext;
import javax.xml.bind.JAXBException;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.util.*;

/**
 * @author: 10097
 * @date: 2021/8/27 15:07
 * @description:
 */
@Slf4j
@SuppressWarnings("unchecked")
public class ZeApiUtils {

    private static PoolingHttpClientConnectionManager secureConnectionManager;
    private static HttpClientBuilder secureHttpBulder;
    private static RequestConfig requestConfig;
    private static final int MAXCONNECTION = 10;
    private static final int DEFAULTMAXCONNECTION = 5;
    private static String apiUrl = "";

    private static final int SUCCESS_CODE = 200;


    public static <T, R> R exec(T t, R r, HttpMethod method, String apiCode) {
        // TODO: 2021/8/27 优化  1.基础的请求方法没有进行重试 2.初始化的方法使用了太多过时的方法
        log.info("调用浙二医院入参:{}", JSON.toJSONString(t));
        //使用ssl通信
        CloseableHttpClient client = getSecureConnection();
        //将入参对象t转换成xml格式的字符串，在与基础参数拼接成map
        HttpUriRequest request = getRequestMethod(coverParam(t, apiCode), apiUrl, method);
        CloseableHttpResponse response = null;
        try {
            response = client.execute(request);
            if (response.getStatusLine().getStatusCode() == SUCCESS_CODE) {
                HttpEntity entity = response.getEntity();
                String message = EntityUtils.toString(entity, "utf-8");
                r = (R) XMLUtil.xml2bean(r.getClass(), message);
                //打印消息
                log.info("调用浙二医院接口返回:{}", message);
            } else {
                log.info("请求失败");
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new ZeApiException("请求浙二接口失败:" + e.getMessage(), 500);
        } finally {
            if (null != response) {
                try {
                    response.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return r;
    }

    public static void init(ZeApiProperties properties) {
        if (null != secureConnectionManager) {
            return;
        }
        apiUrl = properties.getApiUrl();
        //设置http的状态参数
        requestConfig = RequestConfig.custom()
                .setSocketTimeout(60000)
                .setConnectTimeout(60000)
                .setConnectionRequestTimeout(120000)
                .build();

        try {
            KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
            FileInputStream trustStoreInput = new FileInputStream(new File(properties.getSslPath()));
            trustStore.load(trustStoreInput, properties.getTrustKeyStorePassword().toCharArray());
            KeyStore clientKeyStore = KeyStore.getInstance(KeyStore.getDefaultType());
            FileInputStream clientKeyStoreInput = new FileInputStream(new File(properties.getSslPath()));
            clientKeyStore.load(clientKeyStoreInput, properties.getKeyStorePassword().toCharArray());

            SSLContext sslContext = SSLContexts.custom()
                    .loadTrustMaterial(trustStore, new TrustSelfSignedStrategy())
                    .loadKeyMaterial(clientKeyStore, properties.getKeyPass().toCharArray())
                    .setSecureRandom(new SecureRandom())
                    .useSSL()
                    .build();

            ConnectionSocketFactory plainSocketFactory = new PlainConnectionSocketFactory();
            SSLConnectionSocketFactory sslSocketFactoy = new SSLConnectionSocketFactory(
                    sslContext, new String[]{"SSLv3"}, null,
                    SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);

            Registry<ConnectionSocketFactory> r = RegistryBuilder.<ConnectionSocketFactory>create()
                    //.register("http", plainSocketFactory)
                    .register("https", sslSocketFactoy)
                    .build();

            secureConnectionManager = new PoolingHttpClientConnectionManager(r);
            HttpHost target = new HttpHost(properties.getApiHost(), properties.getApiPost(), "https");
            secureConnectionManager.setMaxTotal(MAXCONNECTION);
            //设置每个Route的连接最大数
            secureConnectionManager.setDefaultMaxPerRoute(DEFAULTMAXCONNECTION);
            //设置指定域的连接最大数
            secureConnectionManager.setMaxPerRoute(new HttpRoute(target), 20);
            secureHttpBulder = HttpClients.custom()
                    .setConnectionManager(secureConnectionManager)
                    .setHostnameVerifier(SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
        } catch (Exception e) {
            throw new Error("Failed to initialize the server-side SSLContext", e);
        }
    }

    private static CloseableHttpClient getSecureConnection() {
        return secureHttpBulder.build();
    }

    private static <T> Map<String, String> coverParam(T t, String apiCode) {
        String request = XMLUtil.bean2xml(t);
        Map<String, String> map = new HashMap<>(10);
        map.put("fid", apiCode);
        map.put("deviceSign", "deviceSign");
        map.put("content", request);
        map.put("version", "5.0");
        map.put("ruleName", apiCode);
        return map;
    }


    private static HttpUriRequest getRequestMethod(Map<String, String> map, String url, HttpMethod method) {
        List<NameValuePair> params = new ArrayList<>();
        Set<Map.Entry<String, String>> entrySet = map.entrySet();
        for (Map.Entry<String, String> e : entrySet) {
            String name = e.getKey();
            String value = e.getValue();
            NameValuePair pair = new BasicNameValuePair(name, value);
            params.add(pair);
        }
        HttpUriRequest reqMethod = null;
        if (HttpMethod.POST.equals(method)) {
            reqMethod = RequestBuilder.post().setUri(url)
                    .addParameters(params.toArray(new NameValuePair[0]))
                    .setConfig(requestConfig).build();
        } else if (HttpMethod.GET.equals(method)) {
            reqMethod = RequestBuilder.get().setUri(url)
                    .addParameters(params.toArray(new NameValuePair[0]))
                    .setConfig(requestConfig).build();
        }
        return reqMethod;
    }
}
